<?php
/**
 * SimplifyPHP Framework
 *
 * This file is part of SimplifyPHP Framework.
 *
 * SimplifyPHP Framework is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * SimplifyPHP Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Rodrigo Rutkoski Rodrigues, <rutkoski@gmail.com>
 */

require_once (SY_DIR . '/lib/pomo/mo.php');

/**
 * L10N.
 *
 * @author Rodrigo Rutkoski Rodrigues, <rutkoski@gmail.com>
 * @package Simplify_Kernel
 */
class LanguageImpl
{

  /**
   * 
   * @var string
   */
  private $default_domain = 'default';

  /**
   * 
   * @var string
   */
  private $default_locale = 'en_US';

  /**
   * Current text domain.
   * 
   * @var string
   */
  private $domain;

  /**
   * Current locale.
   * 
   * @var string
   */
  private $locale;

  /**
   * Loaded text domains.
   * 
   * @var array
   */
  private $text_domains = array();

  public function initialize() {}

  /**
   * Get current locale.
   * 
   * @return string
   */
  public function getLocale()
  {
    return $this->locale ? $this->locale : $this->default_locale;
  }

  /**
   * Get current text domain.
   * 
   * @return string
   */
  public function getTextDomain()
  {
    return $this->domain ? $this->domain : $this->default_domain;
  }

  /**
   * 
   * @param string $domain
   * @param string $path
   * @return array
   */
  public function getAvailableLocales($path = null, $domain = null)
  {
    if (empty($path))
      $path = Simplify::getConfig()->get('l10n:locale_dir');
    
    if (empty($domain))
      $domain = $this->getTextDomain();
    
    $it = new DirectoryIterator($path);
    
    $locales = array(
      '' => 'Default' 
    );
    
    while ($it->valid()) {
      if (! $it->isDir() && ! $it->isDot() && preg_match('/^' . $domain . '_(.+?)\.mo$/', $it->getFilename(), $o)) {
        $locales[$o[1]] = Inflector::humanize($o[1]);
      }
      
      $it->next();
    }
    
    return $locales;
  }

  /**
   * Load a language file for a given $domain and $locale.
   * 
   * @param string $domain the name of the text domain
   * @param string $locale the locale
   * @param string $path language file path
   * @param string $codeset the codeset for the language file
   * @return void
   */
  public function loadLanguageFile($locale = null, $domain = null, $path = null, $codeset = null)
  {
    if (empty($domain))
      $domain = $this->getTextDomain();
    
    if (empty($path))
      $path = Simplify::getConfig()->get('l10n:locale_dir');
    
    if (empty($locale))
      $locale = $this->getLocale();
    
    if (empty($codeset))
      $codeset = Simplify::getConfig()->get('l10n:codeset');
    
    $file = sy_fix_path($path . '/' . $domain . '_' . $locale . '.mo');
    
    if (file_exists($file)) {
      $mo = new MO();
      $mo->import_from_file($file);
      
      if (isset($this->text_domains[$domain . '_' . $locale]->l10n)) {
        $mo->merge_with($this->text_domains[$domain . '_' . $locale]->l10n);
      }
    }
    else {
      $mo = new NOOP_Translations();
    }
    
    $this->text_domains[$domain . '_' . $locale]->l10n = $mo;
    $this->text_domains[$domain . '_' . $locale]->codeset = $codeset;
  }

  /**
   * Set current locale.
   * 
   * @param string $locale the locale
   * @return void
   */
  public function setLocale($locale = null)
  {
    if (empty($locale))
      $locale = $this->default_locale;
    
    $this->locale = $locale;
    
    setlocale(6, $locale);
  }

  /**
   * Set current text domain.
   * 
   * @param string $domain the text domain
   * @return void
   */
  public function setTextDomain($domain = null)
  {
    if (empty($domain))
      $domain = $this->default_domain;
    
    $this->domain = $domain;
  }

  /**
   * Lookup a message in the current domain.
   */
  public function gettext($msgid)
  {
    return $this->encode($this->getReader()->translate($msgid));
  }

  /**
   * Plural version of gettext.
   */
  public function ngettext($single, $plural, $number)
  {
    return $this->encode($this->getReader()->translate_plural($single, $plural, $number));
  }

  /**
   * Override the current domain.
   */
  public function dgettext($domain, $msgid)
  {
    return $this->encode($this->getReader($domain)->translate($msgid, $domain));
  }

  /**
   * Plural version of dgettext.
   */
  public function dngettext($domain, $single, $plural, $number)
  {
    return $this->encode($this->getReader($domain)->translate_plural($single, $plural, $number, $domain));
  }

  /**
   * Utility function to get a StreamReader for the given text domain.
   */
  protected function getReader($domain = null, $locale = null)
  {
    if (empty($domain))
      $domain = $this->getTextDomain();
    
    if (empty($locale))
      $locale = $this->getLocale();
    
    if (! isset($this->text_domains[$domain . '_' . $locale])) {
      $this->loadLanguageFile($locale, $domain);
    }
    
    return $this->text_domains[$domain . '_' . $locale]->l10n;
  }

  /**
   * Get the codeset for the given domain.
   */
  protected function getCodeset($domain = null)
  {
    if (! isset($domain))
      $domain = $this->getTextDomain();
    
    $locale = $this->getLocale();
    
    return (isset($this->text_domains[$domain . '_' . $locale])) ? $this->text_domains[$domain . '_' . $locale]->codeset : ini_get('mbstring.internal_encoding');
  }

  /**
   * Convert the given string to the encoding set by bind_textdomain_codeset.
   */
  protected function encode($text)
  {
    $source_encoding = mb_detect_encoding($text);
    $target_encoding = $this->getCodeset();
    
    if ($source_encoding != $target_encoding) {
      return mb_convert_encoding($text, $target_encoding, $source_encoding);
    }
    else {
      return $text;
    }
  }

}

?>